module nade.syntax;

import std.stdio;
import std.string;
public import std.metastrings;
import std.conv;
import std.ctype;

import nade.array;
import nade.environment;


char take(ref string s)
{
	if(s.length == 0) throw new Mismatch;
	char c = s[0];
	s = s[1 .. $];
	return c;
}


class Mismatch {
	this() { version(Trace) writefln("Mismatch"); }
}

alias void[] delegate(ref string, Environment env) Rule;

struct Ruleset {
	string			name;
	Rule[]			rules;

	void[] parse(ref string input, Environment env)
	{
		string temp = input;
		foreach_reverse(k, rule; rules)
		{
			try {
				version(Trace) writefln("Rule %s(%d) <- %s", name, k, input);
				auto r = rule(input, env);
				version(Trace) writefln("Match %s(%d)", name, k);
				return r;
			}
			catch(Mismatch) {
				input = temp;
			}
		}

		throw new Mismatch;
	}
}


void parseSpaces(ref string input, Environment env)
{
	input = input.stripl();
}

void parseCharacter(ref string input, Environment env, char character)
{
	version(Trace) writefln("Rule Character(%s) <- %s", character, input);
	if(input.take() != character) throw new Mismatch;
	version(Trace) writefln("Match Character(%s)", character);
}

void parseLiteral(ref string input, Environment env, string literal)
{
	version(Trace) writefln("Rule Literal(%s) <- %s", literal, input);
	if(literal.length == 0) return input;
	if(input.length == 0) throw new Mismatch;
	parseCharacter(input, env, literal[0]);
	parseLiteral(input, env, literal[1 .. $]);
	version(Trace) writefln("Match Literal(%s)", literal);
}

char parsePattern(ref string input, Environment env, string pattern)
{
	version(Trace) writefln("Rule Pattern(%s) <- %s", pattern, input);
	char a = input.take();
	if(!inPattern(a, pattern)) throw new Mismatch;
	version(Trace) writefln("Match Pattern(%s) -> %s", pattern, a);
	return a;
}


template Rules(R...)
{
	static if(R.length == 1)
	{
		alias Format!(cast(char[])"(ref string input, Environment env) { %s }", R[0]) Rules;
	}
	else
	{
		alias Format!(cast(char[])"%s,\n%s", Rules!(R[0]), Rules!(R[1 .. $])) Rules;
	}
}

template MRule(string N, T, R...) {
	mixin(Format!(cast(char[])"T parse%s(ref string input, Environment env) { return Ruleset!(T)(N, [%s]).parse(input, env); }", N, Rules!(R)));
}
